﻿#if !SILVERLIGHT

/*
 * CommandLegendNode
 * 
 * initial implementation : 20.10.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Data.Common;
using System.Collections.Generic;
using Petra.Gis;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.DataServices;
using Petra.Gis.DataServices.Metadata;
using Petra.Gis.ReferenceSystemServices.Transformations;


namespace Petra.Gis.RenderingServices.PlanarBase 
{

  /// <summary>
  /// Legend node that can display <c>SpatialDbCommand</c> (select) in map
  /// node supports only <c>SpatialDbCommand</c> data type with valid <c>FeatureMetadata</c>
  /// </summary>
  public class CommandLegendNode : DataLegendNodeBase
  {


    #region ctor

    /// <summary>
    /// xaml constr.
    /// </summary>
    public CommandLegendNode() 
      :base(){ }





    public CommandLegendNode(LeafLegendNode parent, SpatialDbCommand data, string title, RendererBase[] renderers)
      : base(parent, title)
    {
      //_nodeType = LegendNodeType.DataNode;
      this.FeatureSet = data;

      foreach (RendererBase r in renderers)
        this.Renderers.Add(r);

    }


    #endregion ctor



    public override void RenderData()
    {

      // if no renderes, then exist
      if (this.Renderers.Count == 0)
        throw new ApplicationException("no renderer available to render data");

      // prepare renderers
      // prepares also transforms in here
      // load all visual attributes (if any...)
      prepareRenders();

      // prepare spatial filter(s) for data
      // if spatialcommand refers to data with more than 1 spatial fiels
      // spatial filter must be defined for each spatial field
      if (this.Legend.Map.ApplyMapRangeAsSpatialFilter)
      {
        FeatureSpatialFilter spatialFilters = createSpatialFilter();

        // create parameter with FeatureSpatialFilter
        if (featureSet.Parameters.ContainsKey("Petra.Gis.DataServices.FeatureSpatialFilter"))
          featureSet.Parameters["Petra.Gis.DataServices.FeatureSpatialFilter"].Value = spatialFilters;

        else
        {
          SptlDbParameter tmpParam = (SptlDbParameter)_data.CreateParameter();
          tmpParam.ParameterName = "Petra.Gis.DataServices.FeatureSpatialFilter";
          tmpParam.Value = spatialFilters;
          _data.Parameters.Add(tmpParam.ParameterName, tmpParam);
        }
      }

      // load data as DataReader
      SpatialDbDataReader dr = (SpatialDbDataReader)((SpatialDbCommand)featureSet).ExecuteReader();

      // if nothing to render exit
      if(!dr.HasRows)
        return;

      //// prepare SRSTransforms for each spatial field in data
      //// as dictionary for fast search
      //Dictionary<string, SRSTransform> spatialFields = new Dictionary<string, SRSTransform>();
      //foreach (SpatialFieldMetadata gfm in _data.GetFeatureMetadata().GeometryFieldsMetadata)
      //{
      //  // if this field is in datareader then
      //  // prepare its transforms and renderers
      //  try
      //  {
      //    dr.GetOrdinal(gfm.Name);

      //    // first transforms...
      //    // if spatial data domain is grid (Grid geometry) then prepare also reverse transform
      //    // into local cache
      //    // otherwise get appropriate (non reverse only) transform for each spatial field
      //    if (gfm.DomainType == DomainGeometryTypeEnum.Grid)
      //      this.Legend.Map.SRSTransformCache.GetTransform(gfm.SRS, true);

      //    spatialFields.Add(gfm.Name, this.Legend.Map.SRSTransformCache.GetTransform(gfm.SRS, false));

      //  }
      //  catch { }
      //}

      //// attach transforms to renderers
      //foreach (RendererBase r in _renderers)
      //{
      //  if (r.SpatialFieldName != "")
      //  {
      //    r.ForwardTransform = spatialFields[r.SpatialFieldName];
      //  }
      //  else
      //    r.ForwardTransform = spatialFields[r.SpatialFieldName];
      //}

      //step over records and paint into map...
      GeometryBase tmpG;
      //SRSTransform revTr;
      while (dr.Read())
      {
        //foreach (KeyValuePair<string, SRSTransform> sft in spatialFields)
        //{
          foreach (RendererBase r in Renderers)
          {
            // if renderer has visual attributes
            // load first data source values for visual parameters
            if (_hasVisualAttributes)
            {
              foreach (KeyValuePair<string, object> prm in VisualAttributes)
                VisualAttributes[prm.Key] = dr.GetValue(dr.GetOrdinal(prm.Key));
            }

            // if this field has renderer
            //if (r.SpatialFieldName.ToLower() == sft.Key.ToLower() || r.SpatialFieldName == "")
            //{
              //tmpG = (GeometryBase)dr.GetValue(dr.GetOrdinal(sft.Key));
              tmpG = (GeometryBase)dr.GetValue(dr.GetOrdinal(r.SpatialFieldName));
              // for grid data dont do transform now, but attach transform 
              // and reverse transform into grid object and call RenderGeometry()
              //if (tmpG is Grid)
              //{
              //  if (sft.Value.IsInversible)
              //    revTr = sft.Value.Inverse();
              //  else
              //    revTr = this.Legend.Map.SRSTransformCache.GetTransform(sft.Value.Source, true);

              //  r.RenderGeometry(tmpG, sft.Value, revTr);
              //}
              //// otherwise do normal transform of vector geometry
              //else
                //r.RenderGeometry(tmpG, sft.Value, null);
                r.RenderGeometry(tmpG);
            //}
          }
        //}
        //return;
      }
    }


    private SpatialDbCommand _data;

    /// <summary>
    /// set/get <c>SpatialDbCommand</c> as source of data for rendering of this node in map
    /// </summary>
    protected override IFeatureSet featureSet
    {
      get { return _data; }
      set 
      {
        //if (value == null)
        //  throw new ArgumentException("featureSet is null");

        //if (value.Metadata == null)
        //  throw new ArgumentException("invalid command, missing feature metadata");

        //if (value.Metadata.GeometryFieldsMetadata.Length < 1)
        //  throw new ArgumentException("invalid command, no geometries found");

        validateIFeatureSet(value);

        //cast to spatial command...
        _data = (SpatialDbCommand)value;

        //if project is opened then resolve cache
        if(Petra.Gis.Environment.ActiveProject != null)
          resolveCachedItems(_data);

        prepareRenders();
      }
    }





    new public SpatialDbCommand FeatureSet
    {
      get { return (SpatialDbCommand)featureSet; }
      set { featureSet = value; }
    }




    protected void resolveCachedItems(SpatialDbCommand cmd)
    {

      SpatialDbConnection sdc = (SpatialDbConnection)cmd.Connection;

      SpatialDbConnection cachedConn;
      if (Petra.Gis.Environment.ActiveProject.DataConnections.ContainsKey(sdc.Name))
        cachedConn = Petra.Gis.Environment.ActiveProject.DataConnections[sdc.Name];
      else
        cachedConn = null;

      // if not exists add as new
      if (cachedConn == null)
        Petra.Gis.Environment.ActiveProject.DataConnections.Add(sdc.Name, sdc);
      else
      {
        // if connection exist in cache and is same as new one, then use cached one
        // this is because xaml restore. where original single object reference
        // is restored as multiple object instances...
        if (sdc.EqualParams(cachedConn))
          cmd.Connection = cachedConn;
        else
          // if different throw error here
          // because there cant be 2 different connections with same name in cache
          // nor replacing new by cached one might corrupt new command
          throw new ArgumentException("connection pool already contains different connection with name {'" + sdc.Name + "'}" +
                                       ". Please provide different name");
      }

      // do same with command
      // one command can be used multiple times
      int idx = Petra.Gis.Environment.ActiveProject.DataCommands.IndexOf(cmd);
      if (idx >= 0)
        _data = Petra.Gis.Environment.ActiveProject.DataCommands[idx];
      else
        Petra.Gis.Environment.ActiveProject.DataCommands.Add(cmd);
    }


  }

}

#endif //SILVERLIGHT